昨天的結尾我們稍微提到 MVVM 架構,在開始動手製作 App 之前,讓我們先來深入了解一下 MVVM 吧!MVVM(Model-View-ViewModel)是一種常見的架構模式,它能幫助我們更好地分離邏輯與 View,使 App 的維護與擴展變得更加容易。
MVVM 是一種將資料處理與 UI 呈現分離的架構模式。它主要由三個部分組成:
在 MVVM 架構中,View 不直接與 Model 互動,而是通過 ViewModel 來實現。這樣做的好處是,我們可以更好地測試和管理 App 中的邏輯,因為資料和 UI 是完全分開的。
參考資料:SwiftUI MVVM
SwiftUI 的設計理念非常適合與 MVVM 架構搭配使用。透過 SwiftUI 的宣告式語法,我們可以輕鬆地建立 View,並利用 @StateObject
和 @Published
等屬性來管理 View 與 ViewModel 之間的資料同步,讓 UI 隨著資料變動而自動更新。
@Published
是 Swift 中的一個屬性包裝器,它用來標記那些可能會隨時改變且需要通知 View 更新的屬性。當我們在 ViewModel 中使用 @Published
來修飾一個屬性時,任何與這個屬性相關的 View 都會在屬性值變化時自動重新渲染。這樣,我們無需手動更新 UI,資料的變動會自動反應在 View 上。例如:
@Published var items: [Item] = [
Item(name: "牛奶", quantity: 2),
Item(name: "麵包", quantity: 1)
]
在這個例子中,當 items 陣列中的資料變化時,使用這個資料的 View 會自動更新,顯示最新的內容。
@StateObject
是 SwiftUI 中用來管理 ViewModel 的屬性包裝器。當我們在 View 中使用 @StateObject
來初始化一個 ViewModel 時,SwiftUI 會負責管理這個物件的生命週期,並在 View 更新時保留這個物件。即使 View 被多次重建,@StateObject
所管理的 ViewModel 也會保持不變。例如:
@StateObject var viewModel = ItemViewModel()
這段程式碼讓 ItemViewModel 只會被初始化一次,並在 View 的生命週期內持續存在,這樣我們的資料狀態就能在多次 UI 更新中保持一致。
參考資料:SwiftUI view 的生命週期影響 StateObject & State property 儲存的資料
假設我們要顯示一個家用品清單,首先我們會定義一個 Item 模型來代表每一個家用品:
struct Item: Identifiable {
var id = UUID()
var name: String
var quantity: Int
}
在這裡,我們使用了 Identifiable protocols 來讓每個 Item 都有一個唯一的 id。這個 id 屬性允許 SwiftUI 能夠唯一識別每一個資料項目。當我們在使用像 List
或 ForEach
這樣的元件來顯示資料時,SwiftUI 需要這樣的唯一識別來有效追蹤和管理資料的變化。即使兩個 Item 的 name 和 quantity 相同,由於 id 是唯一的,SwiftUI 還是能夠正確地辨識它們是不同的項目。
參考資料:
接著,我們需要建立一個 ViewModel 來管理這些資料,並且將它們提供給 View:
class ItemViewModel: ObservableObject {
@Published var items: [Item] = [
Item(name: "牛奶", quantity: 2),
Item(name: "麵包", quantity: 1)
]
func addItem(name: String, quantity: Int) {
let newItem = Item(name: name, quantity: quantity)
items.append(newItem)
}
}
這裡我們使用了剛剛提到的 @Published 屬性來修飾 items,當 items 的值改變時,會自動通知與之綁定的 View 進行更新。
ObservableObject 是 SwiftUI 中用來協調資料變化與 View 更新的 protocol。在宣告型別時,必須要遵從 ObservableObject 這個 protocol ,這時就可以讓需要自動更新的物件使用 @Published
來標記。這樣當這些屬性變化時,任何觀察這個物件的 View 都會被通知並更新顯示。這使得 ViewModel 可以作為資料和 UI 之間的橋樑,確保資料變化能夠即時更新在 View 上。
在 SwiftUI 中,@StateObject
和 @ObservedObject
都是用來觀察 ObservableObject 的,但它們有一些關鍵的差異:
@StateObject
。@StateObject
只會在 View 的生命週期內建立一次 ObservableObject,並且當 View 銷毀時,這個物件也會被銷毀。@ObservedObject
不會建立或擁有 ObservableObject,它只負責觀察這個物件的變化。在這個範例中,ItemViewModel 遵從了 ObservableObject 協議,讓 SwiftUI 能夠監聽 items 的變化,並自動更新使用這些資料的 View。
我們使用 @StateObject
來管理 ItemViewModel。這是因為 ContentView 是第一個建立並使用這個 ViewModel 的 UI,我們希望這個 UI 負責管理 ViewModel 的生命週期。
使用 @StateObject
可以避免重複建立 ViewModel,即使 ContentView 被重新渲染,它仍然會保持對同一個 ItemViewModel 的引用,這樣可以避免潛在的問題。
接下來,我們的 View 就可以使用 ItemViewModel 來顯示這些資料:
struct ContentView: View {
@StateObject var viewModel = ItemViewModel()
var body: some View {
List(viewModel.items) { item in
Text("\(item.name) - \(item.quantity)")
}
}
}
在這個範例中,ContentView 使用了 @StateObject
來初始化 ItemViewModel,並通過 List 元件將資料顯示在 UI 上。
MVVM 可以讓我們的程式更加模組化和易於測試。因為 View 與 ViewModel 之間的清晰分離,我們可以單獨測試 ViewModel 中的商業邏輯,而不需要與 UI 綁定在一起。同時,這種模式也讓我們的 App 更容易擴展,因為邏輯與 View 是完全獨立的。
今天我們練習在 SwiftUI 中使用 MVVM 架構,這是建立大型 App 時非常重要的一個模式。透過 MVVM,我們可以更好地管理 App 中的資料和 View 之間的關係,讓我們的程式碼更具結構性和可維護性。接下來,我們會繼續運用這個架構來開發我們的家用品庫存 App,敬請期待!